package tcp

import (
	"ams/dao"
	"encoding/json"
	"fmt"
	log "github.com/sirupsen/logrus"
	"lib/id"
	"lib/structs/ams"
	"lib/structs/tables"
	"lib/utils"
	"time"
)

var handler = new(msgHandler)

type msgHandler struct {
}

// HandUp 处理举手信息
func (s msgHandler) HandUp(in ams.TcpResponse) (cid int64, err error) {
	if in.ResCode != ams.ResCode.HandsUp {
		err = fmt.Errorf("is not HandUp msg,in:[%v]", in)
		return
	}
	var msg ams.HandsUpMsg
	_ = json.Unmarshal([]byte(in.ResContent), &msg)
	if msg.UniqueForNow == msg.UniqueForDB && msg.UniqueForNow == msg.UniqueForFile {
		// 当前计算值和另外三处一致，说明是正常情况
		cid, err = s.handUpByOK(msg)
		if err != nil {
			err = fmt.Errorf("handUpByOK msg:[%v],err:[%v]", msg, err)
			return
		}
	} else {
		err = s.handUpByAbnormal(msg, "")
		if err != nil {
			err = fmt.Errorf("handUpByAbnormal msg:[%v],err:[%v]", msg, err)
			return
		}
	}
	return
}

// GetClientInfo 设置客户端ID，并将ID发送给客户端
func (s msgHandler) GetClientInfo(uCode string) (m tables.AmsClient, err error) {
	m, err = dao.ClientDao.FindByUniqueCode(uCode)
	if err != nil {
		err = fmt.Errorf("dao.ClientDao.FindByUniqueCode err:[%v]", uCode)
		return
	}
	if m.Id == 0 {
		err = fmt.Errorf("uCode:[%s] is not found clientInfo", uCode)
		return
	}
	return
}

// checkPluginList 检查插件信息是否存在变化，返回需要更新的插件信息
func (s msgHandler) checkPluginList(in ams.TcpResponse) (li []tables.AmsPlugin, err error) {
	var (
		uLi  []ams.PluginInfo
		uMap = make(map[string]ams.PluginInfo)
		dbLi []tables.AmsPlugin
	)
	_ = json.Unmarshal([]byte(in.ResContent), &uLi)
	dbLi, err = dao.PluginDao.FindList()
	if err != nil {
		err = fmt.Errorf("dao.PluginDao.FindList err:[%v]", err)
		return
	}
	for _, item := range uLi {
		uMap[item.Name] = item
	}
	for _, item := range dbLi {
		// 是否为必须插件，如果是就检查 MD5 和版本
		if item.IsRequired {
			if _, ok := uMap[item.Name]; !ok {
				li = append(li, item)
				continue
			}
			if item.CheckMd5 && !utils.CompareByTrimAndToLower(item.Md5, uMap[item.Name].Md5) {
				li = append(li, item)
				continue
			}
			if item.CheckVersion && !utils.CompareByTrimAndToLower(item.Version, uMap[item.Name].Version) {
				li = append(li, item)
				continue
			}
		}
	}
	return li, nil
}

// getPluginUpdateInstruct 获取插件更新指令内容
func (s msgHandler) getPluginUpdateInstruct(li []tables.AmsPlugin) (inst ams.PluginInstruct) {
	if len(li) > 0 {
		flist := make([]ams.PluginForDownLoadFile, len(li))
		isMain := false
		for i, item := range li {
			flist[i] = ams.PluginForDownLoadFile{
				FileUri: item.DownloadUrl,
				Target:  item.UnTarget,
				ZipMd5:  item.ZipMd5,
			}
			if item.Name == ams.ClientMain {
				isMain = true
			}
		}
		instArgs := ams.PluginForUpdate{
			FileList: flist,
		}
		if isMain {
			instArgs.RestartServer = ams.ServerName
		}
		inst = ams.PluginInstruct{
			Cmd:             ams.Update,
			Args:            []string{"multiple", fmt.Sprintf("-in=%s", utils.Obj2Str(instArgs))},
			Env:             ams.ClientPluginFolder,
			TimeoutDuration: 10,
		}
	}
	return
}

// checkPolicyList 更新策略
func (s msgHandler) checkPolicyList(in ams.TcpResponse) (li []tables.AmsPolicy, err error) {
	var (
		uLi  utils.StringArrayInt64
		dbLi []tables.AmsPolicy
	)
	_ = json.Unmarshal([]byte(in.ResContent), &uLi)
	dbLi = dao.Policies
	for _, item := range dbLi {
		if s.checkPolicyValidity(item, in.UniqueCode) {
			li = append(li, item)
		}
	}
	return li, nil
}

// checkPolicyValidity 检查策略有效性，主要考虑范围、时效
func (s msgHandler) checkPolicyValidity(m tables.AmsPolicy, ucode string) (b bool) {
	b = s.checkPolicyValidityFormTime(m) && s.checkPolicyValidityFormScope(m, ucode)
	return
}

func (s msgHandler) checkPolicyValidityFormTime(m tables.AmsPolicy) (b bool) {
	if m.Times == 2 {
		return true
	}
	now := time.Now().Unix()
	if now < m.StartTime && now < m.EndTime {
		return true
	}
	return false
}

func (s msgHandler) checkPolicyValidityFormScope(m tables.AmsPolicy, ucode string) (b bool) {
	if m.ImpactScope == 1 {
		return true
	}
	// todo 附加额外的命中逻辑
	return false
}

func (s msgHandler) heartbeat() {}

func (s msgHandler) uploadCollectLog(in ams.TcpResponse, cid int64) (lid int64, err error) {
	var l ams.ClientLog
	_ = json.Unmarshal([]byte(in.ResContent), &l)
	cl := tables.AmsClientLog{
		Id:          id.GetSnowId(),
		ClientId:    cid,
		Cmd:         l.Cmd,
		Args:        l.Args,
		ElapsedTime: l.ElapsedTime,
		LogCode:     l.LogCode,
		LogContent:  l.LogContent,
		LogTime:     l.CreateAt,
		CreateAt:    time.Now().Unix(),
	}
	// todo 抽取日志核心信息
	switch l.LogCode {
	default:
		cl.LogCore = ""
	}
	err = dao.ClientLogDao.Create(&cl)
	if err != nil {
		err = fmt.Errorf("dao.ClientLogDao.Create err:[%v],in:[%v]", err.Error(), in)
		return
	}
	lid = l.Id
	return
}

func (s msgHandler) updateClientInfo(in ams.HandsUpMsg, dm tables.AmsClient) (err error) {
	b := false
	if dm.IP != in.IP {
		dm.IP = in.IP
	}
	if dm.AssetCode != in.AssetCode {
		dm.AssetCode = in.AssetCode
		b = true
	}
	if dm.ActiveMAC != in.ActiveMAC {
		dm.ActiveMAC = in.ActiveMAC
		b = true
	}
	if dm.BiosSerial != in.BiosSerial {
		dm.BiosSerial = in.BiosSerial
		b = true
	}
	if dm.MainBoardSerial != in.MainBoardSerial {
		dm.MainBoardSerial = in.MainBoardSerial
		b = true
	}
	if dm.HardDiskSerial != in.HardDiskSerial {
		dm.HardDiskSerial = in.HardDiskSerial
		b = true
	}
	if dm.Hostname != in.Hostname {
		dm.Hostname = in.Hostname
		b = true
	}
	if b {
		err = dao.ClientDao.Update(dm)
	}
	return err
}

// handUpByOK 正常情况下的举手
func (s msgHandler) handUpByOK(in ams.HandsUpMsg) (cid int64, err error) {
	var m tables.AmsClient
	m, err = dao.ClientDao.FindByUniqueCode(in.UniqueForFile)
	if err != nil {
		return 0, fmt.Errorf("FindByUniqueCode err:[%s]", err)
	}
	if m.Id != 0 {
		// 更新
		if err = s.updateClientInfo(in, m); err != nil {
			return 0, fmt.Errorf("updateClientInfo err:[%s]", err)
		}
		return m.Id, nil
	} else {
		// 添加
		m = tables.AmsClient{
			Id:              id.GetSnowId(),
			UniqueCode:      in.UniqueForFile,
			AssetCode:       in.AssetCode,
			IP:              in.IP,
			ActiveMAC:       in.ActiveMAC,
			BiosSerial:      in.BiosSerial,
			MainBoardSerial: in.MainBoardSerial,
			HardDiskSerial:  in.HardDiskSerial,
			Hostname:        in.Hostname,
			CreatedAt:       time.Now().Unix(),
		}
		ms := dao.Cache.CheckClientSimilarity(m)
		if ms == "" {
			if err = dao.ClientDao.Create(&m); err != nil {
				return 0, fmt.Errorf("create client err:[%s],in:[%v]", err, m)
			}
			return m.Id, nil
		} else {
			err = s.handUpByAbnormal(in, ms)
			if err != nil {
				return 0, fmt.Errorf("handUpByAbnormal err:[%s],in:[%v]", err, m)
			}
			log.Infof("存在高度相识的设备:[%s],HandsUpMsg:[%v]", ms, in)
			return 0, nil
		}
	}
}

// handUpByErr 异常情况下的举手
func (s msgHandler) handUpByAbnormal(in ams.HandsUpMsg, mapSimilarity string) (err error) {
	var m tables.AmsClientAbnormal
	if m, err = dao.ClientDao.FindAbnormalByUniqueCode(in.UniqueForFile); err != nil {
		return fmt.Errorf("FindAbnormalByUniqueCode err:[%s]", err)
	}
	if m.Id == 0 {
		m = tables.AmsClientAbnormal{
			Id:              id.GetSnowId(),
			UniqueForNow:    in.UniqueForNow,
			UniqueForDB:     in.UniqueForDB,
			UniqueForFile:   in.UniqueForFile,
			AssetCode:       in.AssetCode,
			IP:              in.IP,
			ActiveMAC:       in.ActiveMAC,
			BiosSerial:      in.BiosSerial,
			MainBoardSerial: in.MainBoardSerial,
			HardDiskSerial:  in.HardDiskSerial,
			Hostname:        in.Hostname,
			UpdatedAt:       0,
			MapSimilarity:   mapSimilarity,
		}
		if err = dao.ClientDao.CreateAbnormal(&m); err != nil {
			return fmt.Errorf("CreateAbnormal err:[%s]", err)
		}
	} else {
		m.UniqueForNow = in.UniqueForNow
		m.UniqueForDB = in.UniqueForDB
		m.UniqueForFile = in.UniqueForFile
		m.AssetCode = in.AssetCode
		m.IP = in.IP
		m.ActiveMAC = in.ActiveMAC
		m.BiosSerial = in.BiosSerial
		m.MainBoardSerial = in.MainBoardSerial
		m.HardDiskSerial = in.HardDiskSerial
		m.Hostname = in.Hostname
		m.MapSimilarity = mapSimilarity
		if err = dao.ClientDao.UpdateAbnormal(m); err != nil {
			return fmt.Errorf("UpdateAbnormal err:[%s]", err)
		}
	}
	return nil
}
